Skip to content

fix(mcp): MCP Instrumentation: streamablehttp_client Parameter Corruption #3199

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 3 commits into from
Aug 13, 2025

Conversation

feng-95
Copy link
Contributor

@feng-95 feng-95 commented Jul 28, 2025

  • I have added tests that cover my changes.
  • If adding a new instrumentation or changing an existing one, I've added screenshots from some observability platform showing the change.
  • PR name follows conventional commits format: feat(instrumentation): ... or fix(instrumentation): ....
  • (If applicable) I have updated the documentation accordingly.
image

When using the MCP Instrumentation to instrument the mcp streamablehttp_client, the parameters returned by the instrumentation are incorrect. The streamablehttp_client returns

     read_stream,
     write_stream,
     transport.get_session_id,

https://github.com/modelcontextprotocol/python-sdk/blob/v1.9.0/src/mcp/client/streamable_http.py#L486
But the instrumentated function only returns read_stream, write_stream.


Important

Fixes parameter return issue in MCP Instrumentation's streamablehttp_client by correctly handling get_session_id_callback in instrumentation.py.

  • Behavior:
    • Fixes parameter return issue in traced_method within _transport_wrapper in instrumentation.py.
    • Correctly yields get_session_id_callback when present, aligning with streamablehttp_client's expected return values.
    • Adds exception handling to log warnings and yield results in case of errors.
  • Misc:
    • Adds logging for exceptions in _transport_wrapper to aid debugging.

This description was created by Ellipsis for baef8ef. You can customize this summary. It will automatically update as commits are pushed.

Summary by CodeRabbit

  • Bug Fixes

    • Improved robustness when handling different stream-return formats from instrumented transports; the instrumentation now gracefully handles both two- and three-item results and falls back safely on errors.
  • Chores

    • Enhanced warning and error logging to provide better visibility when unpacking or yielding instrumented streams.

@CLAassistant
Copy link

CLAassistant commented Jul 28, 2025

CLA assistant check
All committers have signed the CLA.

Copy link

coderabbitai bot commented Jul 28, 2025

Note

Other AI code review bot(s) detected

CodeRabbit has detected other AI code review bot(s) in this pull request and will avoid duplicating their findings in the review comments. This may lead to a less comprehensive review.

Walkthrough

The MCP instrumentor's _transport_wrapper.traced_method now accepts and yields either 2-tuple or 3-tuple transport results (reader, writer[, session-id-callback]). It updates the return type annotation, adds try/except branches to handle both unpackings, logs warnings on unpack/yield errors, and falls back to yielding the original result on unexpected exceptions.

Changes

Cohort / File(s) Change Summary
MCP Instrumentation Transport Wrapper
packages/opentelemetry-instrumentation-mcp/opentelemetry/instrumentation/mcp/instrumentation.py
Updated return type annotation for _transport_wrapper.traced_method to allow 2- or 3-tuple returns (added Union). Added logic to attempt 2-tuple unpack, fall back to 3-tuple unpack (including session-id callback), broader exception handling with warning logs, and yield original result on errors.

Sequence Diagram(s)

sequenceDiagram
    participant Caller
    participant McpInstrumentor
    participant WrappedTransport

    Caller ->> McpInstrumentor: call traced_method()
    McpInstrumentor ->> WrappedTransport: invoke wrapped transport
    WrappedTransport -->> McpInstrumentor: returns result (2-tuple or 3-tuple or other)
    alt result is 2-tuple
        McpInstrumentor ->> McpInstrumentor: wrap reader/writer
        McpInstrumentor -->> Caller: yield wrapped (reader, writer)
    else result is 3-tuple
        McpInstrumentor ->> McpInstrumentor: wrap reader/writer + include callback
        McpInstrumentor -->> Caller: yield (reader, writer, callback)
    else unpack/error
        McpInstrumentor ->> McpInstrumentor: log warning/error
        McpInstrumentor -->> Caller: yield original result
    end
Loading

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~15 minutes

Poem

🐇 I peeked inside streams where tuples hide,
Two or three pieces, I now handle with pride.
If unpacking trips or surprises unfurl,
I log a small warning and keep safe the whirl.
Hopping through bytes, ever tidy and spry.

✨ Finishing Touches
  • 📝 Generate Docstrings
🧪 Generate unit tests
  • Create PR with unit tests
  • Post copyable unit tests in a comment

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share
🪧 Tips

Chat

There are 3 ways to chat with CodeRabbit:

  • Review comments: Directly reply to a review comment made by CodeRabbit. Example:
    • I pushed a fix in commit <commit_id>, please review it.
    • Open a follow-up GitHub issue for this discussion.
  • Files and specific lines of code (under the "Files changed" tab): Tag @coderabbitai in a new review comment at the desired location with your query.
  • PR comments: Tag @coderabbitai in a new PR comment to ask questions about the PR branch. For the best results, please provide a very specific query, as very limited context is provided in this mode. Examples:
    • @coderabbitai gather interesting stats about this repository and render them as a table. Additionally, render a pie chart showing the language distribution in the codebase.
    • @coderabbitai read the files in the src/scheduler package and generate a class diagram using mermaid and a README in the markdown format.

Support

Need help? Create a ticket on our support page for assistance with any issues or questions.

CodeRabbit Commands (Invoked using PR/Issue comments)

Type @coderabbitai help to get the list of available commands.

Other keywords and placeholders

  • Add @coderabbitai ignore anywhere in the PR description to prevent this PR from being reviewed.
  • Add @coderabbitai summary to generate the high-level summary at a specific location in the PR description.
  • Add @coderabbitai anywhere in the PR title to generate the title automatically.

CodeRabbit Configuration File (.coderabbit.yaml)

  • You can programmatically configure CodeRabbit by adding a .coderabbit.yaml file to the root of your repository.
  • Please see the configuration documentation for more information.
  • If your editor has YAML language server enabled, you can add the path at the top of this file to enable auto-completion and validation: # yaml-language-server: $schema=https://coderabbit.ai/integrations/schema.v2.json

Status, Documentation and Community

  • Visit our Status Page to check the current availability of CodeRabbit.
  • Visit our Documentation for detailed information on how to use CodeRabbit.
  • Join our Discord Community to get help, request features, and share feedback.
  • Follow us on X/Twitter for updates and announcements.

Copy link
Contributor

@ellipsis-dev ellipsis-dev bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Caution

Changes requested ❌

Reviewed everything up to baef8ef in 1 minute and 56 seconds. Click for details.
  • Reviewed 30 lines of code in 1 files
  • Skipped 0 files when reviewing.
  • Skipped posting 1 draft comments. View those below.
  • Modify your settings and rules to customize what types of comments Ellipsis leaves. And don't forget to react with 👍 or 👎 to teach Ellipsis.
1. packages/opentelemetry-instrumentation-mcp/opentelemetry/instrumentation/mcp/instrumentation.py:132
  • Draft comment:
    Using try/except to distinguish a 2-tuple from a 3-tuple is brittle. Consider checking the length of “result” (e.g. using if len(result)==3) to decide which unpacking to use, and update the async generator’s type hint accordingly.
  • Reason this comment was not posted:
    Decided after close inspection that this draft comment was likely wrong and/or not actionable: usefulness confidence = 20% vs. threshold = 50% The comment makes a valid point about using try/except for flow control being somewhat brittle. Using len() would be more explicit and clearer. However, the code was just changed to add better error handling, not to change the core unpacking logic. The suggestion is more of a code quality improvement than a critical issue. The try/except approach could actually be more robust in some cases, as it handles duck typing better than checking lengths. The performance difference is likely negligible. While the len() approach might be marginally clearer, the current approach works and the code changes were focused on error handling, not refactoring the tuple unpacking logic. This comment suggests a valid but non-critical code quality improvement that is tangential to the actual changes being made. Per the rules, we should not keep comments that are purely informative or suggest non-critical refactors.

Workflow ID: wflow_dRm5wie2RSW0OlsL

You can customize Ellipsis by changing your verbosity settings, reacting with 👍 or 👎, replying to comments, or adding code review rules.

@feng-95 feng-95 changed the title fix(instrumentation): MCP Instrumentation: streamablehttp_client Parameter Corruption fix(mcp): MCP Instrumentation: streamablehttp_client Parameter Corruption Jul 28, 2025
@feng-95
Copy link
Contributor Author

feng-95 commented Jul 29, 2025

@fali007 @nirga Could you please take a look to this pr? The instrumented streamable-http mcp-client fails to initialize, , throwing 'ValueError: not enough values to unpack (expected 3, got 2)' errors during startup.

@nirga
Copy link
Member

nirga commented Aug 3, 2025

@feng-95 can you look into the comment from Elipsis?

@feng-95
Copy link
Contributor Author

feng-95 commented Aug 4, 2025

@feng-95 can you look into the comment from Elipsis?

@nirga Updated the type annotation.

Copy link
Member

@nirga nirga left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why can't you yield the same thing? @feng-95

@feng-95
Copy link
Contributor Author

feng-95 commented Aug 4, 2025

Why can't you yield the same thing? @feng-95

Since stdio_client and sse_client yield (read_stream, write_stream), while streamablehttp_client yield (read_stream, write_stream, get_session_id), we need to conditionally yield different responses to support a unified wrapper for all these functions.

I'm unsure whether creating a new wrapper function specifically for streamablehttp_client is the better approach?
@nirga

@feng-95
Copy link
Contributor Author

feng-95 commented Aug 7, 2025

Hi @nirga , could you please take a look to this again? Would you think it will be better to creating a new wrapper function specifically for streamablehttp_client?

Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 0

♻️ Duplicate comments (1)
packages/opentelemetry-instrumentation-mcp/opentelemetry/instrumentation/mcp/instrumentation.py (1)

126-132: Consider standardizing the return type for better API consistency.

While the Union type correctly represents the two possible return types (2-tuple and 3-tuple), having inconsistent return types can make the API harder to use for consumers. They need to handle both cases, which increases complexity.

Consider one of these approaches:

  1. Always return a 3-tuple, using None for the third parameter when not present
  2. Create separate wrapper methods for different transport types

Example of approach 1:

-        ) -> AsyncGenerator[
-            Union[
-                Tuple[InstrumentedStreamReader, InstrumentedStreamWriter],
-                Tuple[InstrumentedStreamReader, InstrumentedStreamWriter, Any]
-            ],
-            None
+        ) -> AsyncGenerator[
+            Tuple[InstrumentedStreamReader, InstrumentedStreamWriter, Any],
+            None
🧹 Nitpick comments (2)
packages/opentelemetry-instrumentation-mcp/opentelemetry/instrumentation/mcp/instrumentation.py (2)

139-147: Consider more specific exception handling in the nested try block.

The nested try-except block catches all exceptions, which might mask other issues beyond tuple unpacking. Consider catching ValueError specifically for the 3-tuple unpacking attempt.

                 except ValueError:
-                    try:
+                    try:
                         read_stream, write_stream, get_session_id_callback = result
                         yield InstrumentedStreamReader(
                             read_stream, tracer
                         ), InstrumentedStreamWriter(write_stream, tracer), get_session_id_callback
-                    except Exception as e:
+                    except ValueError as e:
                         logging.warning(f"mcp instrumentation _transport_wrapper exception: {e}")
                         yield result
+                    except Exception as e:
+                        logging.warning(f"mcp instrumentation _transport_wrapper unexpected exception: {e}")
+                        yield result

146-147: Improve warning messages for better debugging.

The warning messages could be more descriptive to help identify which specific unpacking attempt failed and what the actual result structure was.

-                        logging.warning(f"mcp instrumentation _transport_wrapper exception: {e}")
+                        logging.warning(f"MCP instrumentation: Failed to unpack transport result as 3-tuple: {e}, result type: {type(result)}")
                         yield result
                 except Exception as e:
-                    logging.warning(f"mcp instrumentation transport_wrapper exception: {e}")
+                    logging.warning(f"MCP instrumentation: Unexpected error in transport wrapper: {e}, result type: {type(result)}")

Also applies to: 149-150

📜 Review details

Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 2226fd7 and 8af7327.

📒 Files selected for processing (1)
  • packages/opentelemetry-instrumentation-mcp/opentelemetry/instrumentation/mcp/instrumentation.py (2 hunks)
🧰 Additional context used
🧬 Code Graph Analysis (1)
packages/opentelemetry-instrumentation-mcp/opentelemetry/instrumentation/mcp/instrumentation.py (1)
packages/opentelemetry-instrumentation-mcp/tests/conftest.py (1)
  • tracer (48-49)
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (2)
  • GitHub Check: Lint
  • GitHub Check: Build Packages (3.11)
🔇 Additional comments (2)
packages/opentelemetry-instrumentation-mcp/opentelemetry/instrumentation/mcp/instrumentation.py (2)

3-3: LGTM! The Union import is correctly added.

The import of Union is necessary to support the updated return type annotation that can yield either 2-tuple or 3-tuple results.


133-150: The fix correctly handles both 2-tuple and 3-tuple returns, addressing the original issue.

The implementation properly:

  1. First attempts to unpack as a 2-tuple for stdio_client and sse_client
  2. Falls back to 3-tuple unpacking for streamablehttp_client with the session ID callback
  3. Includes comprehensive error handling with logging for debugging

This resolves the ValueError that was occurring during startup.

@nirga nirga merged commit 9603e1b into traceloop:main Aug 13, 2025
9 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

3 participants